package com.sgb.contract.view.widget;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;

import androidx.annotation.Nullable;

/**
 * 自定义拖拽布局控件
 * @see CustomDragView 始终是根据它的父容器的位置决定它的起始位置坐标点的，因此它的起始位置并不一定是(0,0)contract
 * @author cyj
 */
public class CustomDragView extends FrameLayout {

    /** 控件宽高 */
    private int mWidth = 0;
    private int mHeight = 0;
    /** 所在父容器宽高 */
    private int mParentWidth = 0;
    private int mParentHeight = 0;

    /** 每次MotionEvent.ACTION_DOWN按下的xy坐标点 */
    private float downX = 0f;
    private float downY = 0f;

    /** 是否拖动 */
    private boolean isDrag = false;
    /** 拖拽参数对象 */
    private final DragParams params = new DragParams();
    /** view点击事件 */
    private OnClickListener onClickListener = null;
    /** 拖拽监听器 */
    private DragListener dragListener = null;
    /** 当前视图的touch监听器 */
    private IParentTouchEventListener listener;

    public CustomDragView(Context context) {
        this(context, null);
    }

    public CustomDragView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomDragView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setTag(null);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 测量，获取控件自身真实宽高
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();

        // 动态获取所在父容器宽高，这包含通过代码控制动态的改变了父容器大小，那么这也适用
        ViewGroup mViewGroup = (ViewGroup) getParent();
        if(null != mViewGroup) {
            // Do not use {getViewTreeObserver().addOnPreDrawListener}
            mViewGroup.post(() -> {
                mParentWidth = params.mParentWidth = mViewGroup.getWidth();
                if(mParentWidth > 0 && mParentHeight <= 0) {
                    setInitialPoint(params.x, params.y);
                }
                mParentHeight = params.mParentHeight = mViewGroup.getHeight();
            });
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return false;
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(this.isEnabled()) {

            // 禁用拖拽
            if(null != dragListener && !dragListener.isEnabled()) {
                return false;
            }

            // 此view视图区域内触发 onTouchEvent 事件时，通知其上层布局控件不要拦截 onTouchEvent 事件，解决事件冲突
            getParent().requestDisallowInterceptTouchEvent(true);
            if(null != this.listener) {
                this.listener.onTouch(event);
            }
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    System.out.println("MotionEvent.ACTION_DOWN");
                    isDrag = false;
                    params.downX = downX = event.getX();
                    params.downY = downY = event.getY();
                    if(null != dragListener) {
                        dragListener.onDrag(this, MotionEvent.ACTION_DOWN, params);
                    }
                    break;
                case MotionEvent.ACTION_MOVE:

                    // 当前次滑动距离；扩展：可根据距离实现滑动回弹效果。滑动的距离 = 触摸点滑动到的坐标 - 开始触摸的坐标
                    float xDistance = event.getX() - downX;
                    float yDistance = event.getY() - downY;
                    params.xDistance = xDistance;
                    params.yDistance = yDistance;

                    // 坐标点滑动位移距离大于10，触发拖动
                    if(Math.abs(xDistance) > 10 || Math.abs(yDistance) > 10) {
                        isDrag = true;
                        // 当前布局定位点
                        int l = (int) (getLeft() + xDistance);
                        int r = l + mWidth;
                        int t = (int) (getTop() + yDistance);
                        int b = t + mHeight;

                        // 左右边界判断
                        if(l < 0) {
                            l = 0;
                            r = l + mWidth;
                        } else if (r > mParentWidth) {
                            r = mParentWidth;
                            l = r - mWidth;
                        }

                        // 上下边界判断
                        if(t < 0) {
                            t = 0;
                            b = mHeight;
                        } else if (b > mParentHeight) {
                            b = mParentHeight;
                            t = b - mHeight;
                        }

                        layout(l, t, r, b);

                        if(null != dragListener) {
                            dragListener.onDrag(this, MotionEvent.ACTION_MOVE, params);
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:

                    // view视图的坐标点始终根据视图左上角坐标定位，因此getLeft()/getTop()恰好是xy
                    setLayoutParams();

                    isDrag = false;
                    setPressed(false);

                    if(null != this.onClickListener) {
                        this.onClickListener.onClick(this);
                    }
                    if(null != dragListener) {
                        dragListener.onDrag(this, MotionEvent.ACTION_UP, params);
                    }
                    break;
                case MotionEvent.ACTION_CANCEL:
                    isDrag = false;
                    setPressed(false);
                    break;
                default:
            }
            return true;
        }
        return false;
    }

    /**
     * 设置LayoutParams
     */
    private void setLayoutParams() {
        MarginLayoutParams lp;
        if(getLayoutParams() instanceof FrameLayout.LayoutParams) {
            lp = new FrameLayout.LayoutParams(mWidth, mHeight);
        } else if(getLayoutParams() instanceof LayoutParams) {
            lp = new LayoutParams(mWidth, mHeight);
        } else if(getLayoutParams() instanceof RelativeLayout.LayoutParams) {
            lp = new RelativeLayout.LayoutParams(mWidth, mHeight);
        } else {
            lp = new MarginLayoutParams(mWidth, mHeight);
        }
        lp.setMargins(params.x = getLeft(), params.y = getTop(), 0, 0);
        setLayoutParams(lp);
    }

    /**
     * 设置拖拽初始值坐标点
     * 当父容器的测量未完成时，子view的绘制是无法进行的，因此此处做了父容器测量完成时延时
     * 调用的判断逻辑 {@link CustomDragView#onMeasure}
     */
    public void setInitialPoint(int x, int y) {
        if(isInitial()) {
            params.x = x;
            params.y = y;
            return;
        }
        long timeMillis = SystemClock.uptimeMillis();
        setLeft(params.x);setTop(params.y);
        onTouchEvent(MotionEvent.obtain(timeMillis, timeMillis, MotionEvent.ACTION_UP, x, y, 0));
    }

    /**
     * 是否正处于拖拽状态
     * @return    isDrag
     */
    public boolean isDrag() {
        return isDrag;
    }

    private boolean isInitial() {
        return params.mParentWidth <= 0;
    }

    /**
     * 拦截点击事件，ACTION_CLICK 会被 onTouchEvent 禁用与冲突
     * @param l    l
     */
    @Override
    public void setOnClickListener(@Nullable OnClickListener l) {
        this.onClickListener = l;
    }

    public void setDragListener(DragListener l) {
        this.dragListener = l;
    }

    /**
     * 拖拽监听器
     */
    public interface DragListener {

        /**
         * 拖拽监听回调函数
         * @param motionEventAction    as MotionEvent.ACTION_DOWN / MotionEvent.ACTION_MOVE
         * @param params    {@link DragParams}
         */
        void onDrag(View view, int motionEventAction, DragParams params);

        /**
         * 设置是否启动拖拽
         * @return
         */
        boolean isEnabled();
    }

    /**
     * 拖拽参数
     */
    public static class DragParams {

        /** MotionEvent.ACTION_DOWN 起始点 */
        public float downX = 0f;
        public float downY = 0f;

        /** 滑动距离 */
        public float xDistance = 0f;
        public float yDistance = 0f;

        /** 滑动结束坐标点 */
        public int x = 0;
        public int y = 0;

        /** 所在父容器宽高 */
        public int mParentWidth = 0;
        public int mParentHeight = 0;
    }

    /**
     * 尽量不要使用 {@link this#setBackgroundColor(int)} ，使用它则会导致以下函数使用冲突
     * 建议使用 {@link this#addForegroundView(View)} 与 {@link this#addBackgroundView(View)}
     * @param background
     */
    @Deprecated
    @Override
    public void setBackground(Drawable background) {
        super.setBackground(background);
    }

    /**
     * 添加前景view
     * @param view    View
     */
    public void addForegroundView(View view) {
        addView(view, 0);
    }

    /**
     * 添加后景view
     * @param view    View
     */
    public void addBackgroundView(View view) {
        addView(view, 1);
    }

    public void setBackgroundViewOnClickListener(View.OnClickListener listener) {
        if(null != getChildAt(1)) {
            getChildAt(1).setOnClickListener(listener);
        }
    }

    /**
     * addView
     * @param view
     * @param index
     */
    public void addView(View view, int index) {
        if(null == view) {
            return;
        }
        if(getChildCount() > 0 && indexOfChild(view) >= 0) {
            removeView(view);
        }
        super.addView(view, index);
    }

    /**
     * 添加父类触摸事件监听器
     * @param listener    IParentTouchEventListener
     */
    public void addParentTouchEventListener(IParentTouchEventListener listener) {
        this.listener = listener;
    }

    @Override
    public void setTag(Object tag) {
        if(null == tag) {
            tag = hashCode();
        }
        super.setTag(tag);
    }

    public interface IParentTouchEventListener {
        /**
         * 触摸事件回调
         * @param event
         */
        void onTouch(MotionEvent event);
    }
}
